home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / demos / VGX / shadows / shadows.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  25KB  |  918 lines

  1. /*
  2.  * Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*
  18.     shadows.c - Draw a scene with shadow-volume shadow calculation
  19.         using VGX graphics hardware stencil planes.
  20.  
  21.     Tim Heidmann, Silicon Graphics
  22.  
  23.     Created        June  1, 1988
  24.     Last Edit  November 25, 1991
  25. */
  26.  
  27. #include <stdio.h>
  28. #include <gl.h>
  29. #include <gl/image.h>
  30. #include <device.h>
  31. #include <math.h>
  32. #include <string.h>
  33. #include "trackball.h"
  34. #include "shadows.h"
  35. #include "glo_obj.h"
  36. #include "vect.h"
  37.  
  38. #define PI (3.1415926535)
  39. #define TWOPI (6.283185308)
  40. #define RAD2DEG (180.0/PI)
  41. #define MAXOBJS 10
  42. #define MAXLITES 8
  43. #define STENZERO 128
  44. #define SPACING 1.2
  45. #define SHADOWLENGTH 100.0
  46. #define NEARCLIP 0.25
  47. #define FARCLIP 30.0
  48. #define WINHEIGHT 0.1
  49.  
  50.  
  51. /* Scene description global variables */
  52. float obj_posn[MAXOBJS][3];
  53. float obj_scale[MAXOBJS];
  54. Matrix obj_rotMatrix[MAXOBJS];
  55. int obj_litMaterial[MAXOBJS];
  56. int obj_shadowMaterial[MAXOBJS];
  57. int obj_highMaterial[MAXOBJS];
  58. int obj_texture[MAXOBJS];
  59. glo_ObjPtr obj_drawlist[MAXOBJS];
  60. PolyDataPtr obj_data[MAXOBJS];
  61. int nObjects = 0;
  62.  
  63. float light_posn[MAXLITES][3];
  64. float jitter_light_posn[MAXLITES][3];
  65. float jitter_light_range[MAXLITES][3];
  66. int nLights = 0;
  67.  
  68. /* Viewing, window, and interaction parameters */
  69. float eyex[] = { 0.0, 0.0, 9.0};
  70. float gazex[] = { 0.0, 0.0, 0.0};
  71. Matrix scene_rotMatrix;
  72. float scene_spin[4] = {0.0, 0.0, 0.0, 1.0};
  73. float scene_posn[3] = {0.0, 0.0, 0.0};
  74. int windowParmsSet;
  75. float windowParms[6];
  76. float viewWindow[3] = {WINHEIGHT, NEARCLIP, FARCLIP};
  77. Screencoord lox,hix,loy,hiy;
  78. int win_ox, win_oy, win_sx, win_sy;
  79. float nmx, nmy, omx, omy;
  80. Boolean acFlag = FALSE;
  81. int acCount = -1;
  82. int acFrames = 64;
  83.  
  84. /* Constants to support texture mapping - property arrays, texgen parameters */
  85. float tev_val[] = {TV_NULL};
  86. float tex_val[] =
  87.      {TX_MINFILTER, TX_BILINEAR, TX_MAGFILTER, TX_BILINEAR, TX_NULL};
  88. float tex_sparams[] = {1.0, 0.0, 0.0, 0.0};
  89. float tex_tparams[] = {0.0, 0.0, 1.0, 0.0};
  90. float subdiv_params[] = {10000.0, 0.0, 0.0};
  91.  
  92. /* Program execution flags and modes */
  93. char *sceneFileName;
  94. char *imageFileName;
  95. enum {showScene, showVolumes, showMask, showShadows} showMode=showScene;
  96. int showVolSilEdges;
  97. int showVolShadowEdges;
  98. int showVolObjects;
  99. long CmdMenu;
  100. Boolean canZBuffer, canStencil, canAccum;
  101. int stencilZero;
  102.  
  103. /* What colors to use for what stencil values in showMask display mode */
  104.         /* In/Out: 1/1  2/2  3/3  1/0  2/1  3/2  2/0, 0/0 is clear */
  105. int maskValue[] = {0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x8};
  106. int maskColor[] = {
  107.     0x00ff00ff, /* 1/1 - in == out,   magenta    */
  108.     0x00ff00ff, /* 2/2                           */
  109.     0x00ff00ff, /* 3/3                           */
  110.     0x00ff0000, /* 1/0 - in == out+1, blue       */
  111.     0x00ff0000, /* 2/1                           */
  112.     0x00ff0000, /* 3/2                           */
  113.     0x00c0c000, /* 2/0 - in == out+2, blue-green */
  114. };
  115. int nMasks = (sizeof(maskValue)/sizeof(int));
  116.  
  117. Matrix IdentMat = {
  118.     1.0, 0.0, 0.0, 0.0,
  119.     0.0, 1.0, 0.0, 0.0,
  120.     0.0, 0.0, 1.0, 0.0,
  121.     0.0, 0.0, 0.0, 1.0
  122. };
  123.  
  124.  
  125. /* Forward definitions */
  126. void
  127. ParseArgs(int c, char *v[]);
  128. int
  129. GetTBPosition(int state);
  130. void
  131. UpdateSceneRot();
  132. void
  133. AccumNext();
  134. void
  135. DoCmdMenu(Boolean *quitFlag, Boolean *redrawFlag);
  136. void
  137. GetNextSilEdge(EdgePtr edgeList, int thisEdge, int thisSide,
  138.     int *pNextEdge, int *pNextSide);
  139.  
  140.  
  141.  
  142. main(int argc, char *argv[])
  143. {
  144.     /* These flags control the operation of the main loop:
  145.     done - When true, bust out of the loop and exit;
  146.     refresh - need to redraw the scene;
  147.     okayToBlock - nothing spinning, being moved, or accumulating -
  148.         can wait for event;
  149.     buttonState - combination of LEFT & MID mouse buttons -
  150.         !=0 means scene is being moved.
  151.     spinning - The last movement had some rotation, which continues
  152.         when the mouse button is released.
  153.     acFlag - We want to jitter lights and accumulate when scene not moving.
  154.     acCount - >0: more to accumulate, ==0: done, <0: clear and accumulate.
  155.     */
  156.     Boolean done, refresh, okayToBlock, spinning;
  157.     short val, i, j;
  158.     int buttonState;
  159.     char buf[100];
  160.  
  161.     ParseArgs(argc, argv);
  162.     InitGFX();
  163.     InitDevices();
  164.     InitMenus();
  165.     InitScene();
  166.     qenter(REDRAW,0);
  167.  
  168.     buttonState = GetButtonState();
  169.     spinning = FALSE;
  170.     for (done = FALSE;;) {
  171.     /* Go get all the events or maybe sit and wait for one */
  172.     while(qtest() || okayToBlock) {
  173.         switch(qread(&val)) {
  174.         case REDRAW:
  175.         ShapeWindow(); refresh = TRUE; break;
  176.         case ESCKEY:
  177.         case QKEY:
  178.         case WINQUIT:
  179.         done = TRUE; break;
  180.         case LEFTMOUSE:
  181.         case MIDDLEMOUSE:
  182.         buttonState = GetButtonState(); break;
  183.         case RIGHTMOUSE:
  184.         if (val) DoCmdMenu(&done, &refresh); break;
  185.         default:
  186.         break;
  187.         }
  188.         okayToBlock = FALSE;
  189.     }
  190.  
  191.     /* Now handle all the events */
  192.     if (done) break;
  193.  
  194.     if (buttonState != 0) {
  195.         spinning = GetTBPosition(buttonState); refresh = TRUE;
  196.     }
  197.     if (spinning) {
  198.         UpdateSceneRot(); refresh = TRUE;
  199.     }
  200.     if (refresh) {
  201.         RepositionLights();
  202.         DrawFrame(); swapbuffers(); acCount = -1; refresh = FALSE;
  203.     } else if (acFlag && showMode == showShadows)
  204.         AccumNext();
  205.     
  206.     okayToBlock = (buttonState != 0) ? FALSE :
  207.         spinning ? FALSE :
  208.         (showMode == showShadows && acFlag && acCount != 0) ? FALSE : TRUE;
  209.  
  210.     if (okayToBlock && imageFileName != NULL) {
  211.         sprintf(buf, "scrsave %s %d %d %d %d", imageFileName, 
  212.         lox, hix, loy, hiy);
  213.         system(buf);
  214.         break;
  215.     }
  216.         
  217.     }
  218.  
  219.     FinishDevices();
  220.     FinishGFX();
  221. }
  222.  
  223.  
  224. int
  225. GetButtonState() {
  226.     omx = 2.0 * (getvaluator(MOUSEX) - win_ox) / win_sx - 1.0;
  227.     omy = 2.0 * (getvaluator(MOUSEY) - win_oy) / win_sy - 1.0;
  228.     return getbutton(LEFTMOUSE) << 1 | getbutton(MIDDLEMOUSE);
  229. }
  230.  
  231. int
  232. GetTBPosition(int state) {
  233.     float delta[3];
  234.     int spinning;
  235.  
  236.     nmx = 2.0 * (getvaluator(MOUSEX) - win_ox) / win_sx - 1.0;
  237.     nmy = 2.0 * (getvaluator(MOUSEY) - win_oy) / win_sy - 1.0;
  238.     spinning = FALSE;
  239.  
  240.     switch (state) {
  241.     case 1: /* Middle mouse - rotate */
  242.     trackball(scene_spin, omx, omy, nmx, nmy);
  243.     /* No spin is scene_spin == {0,0,0,1};
  244.        vlength looks at just 1st 3 elements */
  245.     spinning = vlength(scene_spin) > 0.00001;
  246.     break;
  247.     case 2: /* Left mouse - translate */
  248.     vset(delta, nmx - omx, nmy - omy, 0.0);
  249.     vadd(scene_posn, delta, scene_posn);
  250.     break;
  251.     case 3: /* Left & Middle - Track in/out */
  252.     scene_posn[2] += nmy - omy;
  253.     break;
  254.     default:
  255.     break;
  256.     }
  257.     omx = nmx;
  258.     omy = nmy;
  259.  
  260.     return spinning;
  261. }
  262.  
  263. void
  264. UpdateSceneRot() {
  265.     Matrix spinMatrix;
  266.  
  267.     build_rotmatrix(spinMatrix, scene_spin);
  268.     vmultmatrix(scene_rotMatrix, spinMatrix, scene_rotMatrix);
  269. }
  270.  
  271. void
  272. AccumNext() {
  273.     int nDone;
  274.  
  275.     /* Reset accumulation buffer and count if necessary */
  276.     if (acCount == 0) return;
  277.     if (acCount < 0) {
  278.     acbuf(AC_CLEAR, 0.0);
  279.     acCount = acFrames;
  280.     }
  281.  
  282.     /* Accumulate another frame */
  283.     JitterLights();
  284.     DrawFrame();
  285.     readsource(SRC_BACK);
  286.     acbuf(AC_ACCUMULATE, 1.0);
  287.     acCount--;
  288.  
  289.     /* Copy accumulation buffer back to display whenever
  290.        - count reaches specified number (acFrames)
  291.        - count is a power of two, >=4 (4,8,16,32...) */
  292.     nDone = acFrames - acCount;
  293.     if (acCount == 0 || ((nDone&(nDone-1)) == 0 && nDone >= 4)) {
  294.     acbuf(AC_RETURN, 1.0/nDone);
  295.     swapbuffers();
  296.     }
  297. }
  298.  
  299. void
  300. ParseArgs(int c, char *v[]) {
  301.     char *callName;
  302.     int i, iLeft, iRight, iBottom, iTop;
  303.  
  304.     sceneFileName = NULL;
  305.     imageFileName = NULL;
  306.     showMode = showScene;
  307.     showVolSilEdges = TRUE;
  308.     showVolShadowEdges = FALSE;
  309.     showVolObjects = TRUE;
  310.     windowParmsSet = FALSE;
  311.  
  312.     callName = v[0];
  313.     for (c--, v++; c > 0; c--, v++) {
  314.     if (v[0][0] == '-')
  315.         switch (v[0][1]) {
  316.         case 'p':  /* Window position */
  317.         iLeft   = atoi(v[1]);
  318.         iRight  = atoi(v[2]);
  319.         iBottom = atoi(v[3]);
  320.         iTop    = atoi(v[4]);
  321.         prefposition(iLeft, iRight, iBottom, iTop);
  322.         c -= 4; v += 4;
  323.         break;
  324.         case 'w':  /* Window perspective parameters  */
  325.         for (i=0; i<6; i++)
  326.             if (sscanf(v[1 + i], "%f", windowParms + i) != 1)
  327.             BadCall(callName);
  328.         windowParmsSet = TRUE;
  329.         c -= 6; v += 6;
  330.         break;
  331.         case 'o':  /* Save scene as .rgb file */
  332.         imageFileName = v[1];
  333.         v += 1; c -= 1;
  334.         break;
  335.         case 'j': /* Jitter frames */
  336.         acFlag = TRUE;
  337.         acFrames = atoi(v[1]);
  338.         v++; c--;
  339.         break;
  340.         case 'b': /* Block until this program finishes */
  341.         foreground(); break;
  342.         case 'n': /* Normal Scene */
  343.         showMode = showScene; break;
  344.         case 'v': /* Show volumes */
  345.         showMode = showVolumes; break;
  346.         case 'm': /* Show mask */
  347.         showMode = showMask; break;
  348.         case 's': /* Show shadows */
  349.         showMode = showShadows; break;
  350.         case 'e': /* Show silhouette edges */
  351.         showVolSilEdges = TRUE; break;
  352.         case 'f': /* Don't show silhouette edges */
  353.         showVolSilEdges = FALSE; break;
  354.         case 'E': /* Show shadow volume edges */
  355.         showVolShadowEdges = TRUE; break;
  356.         case 'F': /* Don't show shadow volume edges */
  357.         showVolShadowEdges = FALSE; break;
  358.         case 'd': /* Hide objects */
  359.         showVolObjects = FALSE; break;
  360.         default:
  361.         BadCall(callName);
  362.         break;
  363.         }
  364.     else
  365.         /* Parse the scene file name */
  366.         sceneFileName = v[0];
  367.     }
  368.  
  369.     /* Check for valid operations */
  370.     canZBuffer = getgdesc(GD_BITS_NORM_ZBUFFER) > 0;
  371.     canStencil = getgdesc(GD_BITS_STENCIL) > 0;
  372.     canAccum = getgdesc(GD_BITS_ACBUF_HW) > 0;
  373.  
  374.     if (acFlag && !canAccum) {
  375.     fprintf(stderr,
  376.         "This machine has no accumulation buffer, which is required\n");
  377.     fprintf(stderr,
  378.         "to compute soft shadows by jittering the light sources.\n");
  379.     acFlag = FALSE;
  380.     }
  381.     if (!canStencil) {
  382.     fprintf(stderr,
  383.         "This machine has no stencil planes, which are required for\n");
  384.     fprintf(stderr,
  385.         "shadow calculation.\n");
  386.     acFlag = FALSE;
  387.     }
  388.     if (!canZBuffer) {
  389.     fprintf(stderr,
  390.         "This program requires a z-buffer to run.\n");
  391.     exit(1);
  392.     }
  393. }
  394.  
  395. BadCall(char *callName) {
  396.     fprintf(stderr, "usage: %s [options] [<scene filename>]\n", callName);
  397.     fprintf(stderr, "    -p <left right bottom top>  Window position\n");
  398.     fprintf(stderr, "    -w <left right bottom top near far>\n");
  399.     fprintf(stderr, "                                Window parameters\n");
  400.     fprintf(stderr, "    -o <filename>               Output to .rgb file\n");
  401.     fprintf(stderr, "    -j <frames>                 Max Jitter Frames\n");
  402.     fprintf(stderr, "    -b                          Block until program finishes\n");
  403.     fprintf(stderr, "  The following modes are exclusive:\n");
  404.     fprintf(stderr, "    -n         Show normal scene\n");
  405.     fprintf(stderr, "    -v         Show volumes\n");
  406.     fprintf(stderr, "    -m         Show mask\n");
  407.     fprintf(stderr, "    -s         Show shadows\n");
  408.     fprintf(stderr, "  The following flags apply to volume mode only:\n");
  409.     fprintf(stderr, "    -e         Show silhouette edges\n");
  410.     fprintf(stderr, "    -f         Don't show silhouette edges\n");
  411.     fprintf(stderr, "    -E         Show shadow volume edges\n");
  412.     fprintf(stderr, "    -F         Don't show shadow volume edges\n");
  413.     fprintf(stderr, "    -d         Hide objects\n");
  414.     exit(1);
  415. }
  416.  
  417.  
  418. InitGFX()
  419. {
  420. #ifdef DEBUG
  421.     foreground();
  422. #endif
  423.     winopen("Shadow Volumes");
  424.     RGBmode();
  425.     doublebuffer();
  426.     stensize(8);
  427.     acsize(16);
  428.     stencilZero = STENZERO;
  429.     gconfig();
  430.     cpack(0x00000000);
  431.     clear();
  432.     swapbuffers();
  433.  
  434.     zbuffer(TRUE);
  435.     lsetdepth(0x000100, 0x7fffff);
  436.     subpixel(TRUE);
  437.     mmode(MVIEWING);
  438.  
  439.     tevdef(1, 0, tev_val);
  440.     tevbind(TV_ENV0, 1);
  441.     texgen(TX_S, TG_LINEAR, tex_sparams);
  442.     texgen(TX_T, TG_LINEAR, tex_tparams);
  443.     texgen(TX_S, TG_ON, NULL);
  444.     texgen(TX_T, TG_ON, NULL);
  445. }
  446.  
  447. FinishGFX() {
  448.     gexit();
  449. }
  450.  
  451. InitDevices() {
  452.     if (imageFileName == NULL) {
  453.     /* If we are saving an image, don't allow interaction */
  454.     qdevice(LEFTMOUSE);
  455.     qdevice(MIDDLEMOUSE);
  456.     qdevice(RIGHTMOUSE);
  457.     }
  458.     qdevice(ESCKEY);
  459.     qdevice(QKEY);
  460. }
  461.  
  462. FinishDevices() {
  463.     unqdevice(LEFTMOUSE);
  464.     unqdevice(MIDDLEMOUSE);
  465.     unqdevice(RIGHTMOUSE);
  466.     unqdevice(ESCKEY);
  467.     unqdevice(QKEY);
  468. }
  469.  
  470. InitMenus() {
  471.     CmdMenu = defpup("");
  472.     MakeCmdMenu();
  473. }
  474.  
  475. MakeCmdMenu() {
  476.     freepup(CmdMenu);
  477.     CmdMenu=defpup("Shadow Scene %t");
  478.     addtopup(CmdMenu, "Show Objects Only %x3");
  479.     addtopup(CmdMenu, "Show Volumes %x1");
  480.     if (canStencil) {
  481.     addtopup(CmdMenu, "Show Mask %x2");
  482.     addtopup(CmdMenu, "Show Shadows %x4 %l");
  483.     }
  484.     if (canAccum)
  485.     if (acFlag)
  486.         addtopup(CmdMenu, "Turn Soft Shadows Off %x5 %l");
  487.     else
  488.         addtopup(CmdMenu, "Turn Soft Shadows On %x5 %l");
  489.     addtopup(CmdMenu, "Quit %x0");
  490. }
  491.  
  492.  
  493. void
  494. DoCmdMenu(Boolean *quitFlag, Boolean *redrawFlag)
  495. {
  496.     *quitFlag = FALSE;
  497.     *redrawFlag = TRUE;
  498.     switch (dopup(CmdMenu)) {
  499.     case  0: *quitFlag = TRUE;     break;
  500.     case  1: showMode=showVolumes; break;
  501.     case  2: showMode=showMask;    break;
  502.     case  3: showMode=showScene;   break;
  503.     case  4: showMode=showShadows; break;
  504.     case  5: acFlag = !acFlag; MakeCmdMenu(); break;
  505.     default: break;
  506.     }
  507. }
  508.  
  509.  
  510. ShapeWindow()
  511. {
  512.     /* Reshape window, set up perspective and camera transform.
  513.        Provide an undistorted view given any window aspect ratio.
  514.        viewWindow[0]: height of near plane window,
  515.        viewWindow[1]: near clipping plane,
  516.        viewWindow[2]: far clipping plane */
  517.     long xorg, yorg;
  518.     float aspect;
  519.  
  520.     reshapeviewport();
  521.     getviewport(&lox,&hix,&loy,&hiy);
  522.     getorigin(&win_ox, &win_oy);
  523.     lox += win_ox; hix += win_ox;
  524.     loy += win_oy; hiy += win_oy;
  525.     win_sx = hix - lox;
  526.     win_sy = hiy - loy;
  527.     aspect = ((float) win_sx)/win_sy;
  528.     mmode(MPROJECTION);
  529.     if (windowParmsSet)
  530.     window(windowParms[0], windowParms[1],
  531.            windowParms[2], windowParms[3],
  532.            windowParms[4], windowParms[5]);
  533.     else
  534.     if (aspect > 1.0)
  535.         window(-viewWindow[0]*aspect,viewWindow[0]*aspect,
  536.         -viewWindow[0],viewWindow[0],
  537.         viewWindow[1], viewWindow[2]);
  538.     else
  539.         window(-viewWindow[0],viewWindow[0],
  540.         -viewWindow[0]/aspect,viewWindow[0]/aspect,
  541.         viewWindow[1], viewWindow[2]);
  542.  
  543.     lookat(eyex[0], eyex[1], eyex[2], gazex[0], gazex[1], gazex[2], 0);
  544.     mmode(MVIEWING);
  545.     loadmatrix(IdentMat);
  546. }
  547.  
  548.  
  549. DrawFrame() {
  550.     int i, j;
  551.  
  552.     czclear(0x00000000,0x7fffff);
  553.  
  554.     /* Draw the Scene */
  555.     zbuffer(TRUE);
  556.     zwritemask(0xffffffff);
  557.     wmpack(0xffffffff);
  558.     blendfunction(BF_ONE, BF_ZERO);
  559.     for (i=0; i<nLights; i++) lmbind(LIGHT0+i, i+1);
  560.     stencilZero = STENZERO;
  561.  
  562.     switch (showMode) {
  563.     case showScene:
  564.     DrawObjects(obj_litMaterial);
  565.     return;
  566.     case showVolumes:
  567.     if (showVolObjects) DrawObjects(obj_litMaterial);
  568.     break;
  569.     case showMask:
  570.     stencilZero = 0;
  571.     DrawObjects(obj_litMaterial);
  572.     break;
  573.     case showShadows:
  574.     DrawObjects(obj_shadowMaterial);
  575.     break;
  576.     default: ;
  577.     }
  578.  
  579.     /* Draw the shadow volumes */
  580.     lmbind(MATERIAL, 0);
  581.     zwritemask(0x00000000);
  582.  
  583.     switch (showMode) {
  584.     case showVolumes:
  585.     /* To draw the volumes: */
  586.     wmpack(0xffffffff);
  587.     blendfunction(BF_SA, BF_MSA);
  588.     cpack(0x18ffffff);
  589.     DrawShadows(0);
  590.  
  591.     /* Now the silhouette edges */
  592.     if (showVolSilEdges) {
  593.         cpack(0x0000ffff);
  594.         blendfunction(BF_ONE, BF_ZERO);
  595.         DrawSilEdges();
  596.     }
  597.     break;
  598.  
  599.     case showMask:
  600.     /* Build the stencil shadow mattes: */
  601.     sclear(stencilZero);
  602.     wmpack(0x00000000);
  603.     DrawShadows(0);
  604.  
  605.     /* Draw the mask */
  606.     wmpack(0xffffffff);
  607.     zbuffer(FALSE);
  608.     for (i=0; i<nMasks; i++) {
  609.         stencil(TRUE, maskValue[i], SF_EQUAL, 0xff,
  610.         ST_KEEP, ST_KEEP, ST_KEEP);
  611.         DrawSquare(maskColor[i]);
  612.     }
  613.     stencil(FALSE, 0, 0, 0, 0, 0, 0);
  614.     break;
  615.  
  616.     case showShadows:
  617.     for (i=0; i<nLights; i++) {
  618.         /* Set the lights */
  619.         for (j=0; j<nLights; j++) lmbind(LIGHT0+j, (j==i) ? j+1 : 0);
  620.  
  621.         /* Build the stencil shadow mattes: */
  622.         sclear(stencilZero);
  623.         wmpack(0x00000000);
  624.         DrawShadows(i);
  625.  
  626.         /* Draw shadowed elements */
  627.         wmpack(0xffffffff);
  628.         stencil(TRUE, stencilZero, SF_EQUAL, 0xff,
  629.         ST_KEEP, ST_KEEP, ST_KEEP);
  630.         blendfunction(BF_ONE, BF_ONE); 
  631.         DrawObjects(obj_highMaterial);
  632.     }
  633.     stencil(FALSE, 0, 0, 0, 0, 0, 0);
  634.     break;
  635.     }
  636. }
  637.  
  638.  
  639. DrawObjects(int objMaterial[])
  640. {
  641.     /* Draw all objects in this scene */
  642.     int i;
  643.     pushmatrix();
  644.     translate(scene_posn[0], scene_posn[1], scene_posn[2]);
  645.         multmatrix(scene_rotMatrix);
  646.         for (i=0; i<nObjects; i++) {
  647.             pushmatrix();
  648.                 translate(obj_posn[i][0], obj_posn[i][1], obj_posn[i][2]);
  649.         multmatrix(obj_rotMatrix[i]);
  650.                 scale(obj_scale[i], obj_scale[i], obj_scale[i]);
  651.                 lmbind(MATERIAL, objMaterial[i]);
  652.         if (obj_texture[i] > 0) {
  653.             texbind(TX_TEXTURE_0, obj_texture[i]);
  654.             scrsubdivide(SS_DEPTH, subdiv_params);
  655.             glo_DrawObj(obj_drawlist[i]);
  656.             texbind(TX_TEXTURE_0, 0);
  657.             scrsubdivide(SS_OFF, NULL);
  658.         } else
  659.             glo_DrawObj(obj_drawlist[i]);
  660.             popmatrix();
  661.         }
  662.     popmatrix();
  663. }
  664.  
  665.  
  666. DrawShadows(int lighti) {
  667.     float x, y, z;
  668.     int i;
  669.     Matrix myMatrix;
  670.  
  671.     for (i=0; i<nObjects; i++) {
  672.     pushmatrix();
  673.         translate(scene_posn[0], scene_posn[1], scene_posn[2]);
  674.         multmatrix(scene_rotMatrix);
  675.         translate(obj_posn[i][0], obj_posn[i][1], obj_posn[i][2]);
  676.         multmatrix(obj_rotMatrix[i]);
  677.         scale(obj_scale[i], obj_scale[i], obj_scale[i]);
  678.         getmatrix(myMatrix);
  679.     popmatrix();
  680.     DrawSilhouettes(obj_drawlist[i], obj_data[i], myMatrix,
  681.         jitter_light_posn[lighti]);
  682.     }
  683. }
  684.  
  685.  
  686. DrawSquare(long color)
  687. {
  688.     Matrix saveViewMatrix, saveProjMatrix;
  689.  
  690.     getmatrix(saveViewMatrix);
  691.     mmode(MPROJECTION);
  692.     getmatrix(saveProjMatrix);
  693.     mmode(MVIEWING);
  694.  
  695.     cpack(color);
  696.     ortho2(-1.0, 1.0, -1.0, 1.0);
  697.     rectf(-1.0, -1.0, 1.0, 1.0);
  698.  
  699.     mmode(MPROJECTION);
  700.     loadmatrix(saveProjMatrix);
  701.     mmode(MVIEWING);
  702.     loadmatrix(saveViewMatrix);
  703. }
  704.  
  705.  
  706. DrawSilhouettes(myOP, myPDP, xformMatrix, lightx)
  707. glo_ObjPtr myOP;
  708. PolyDataPtr myPDP;
  709. Matrix xformMatrix;
  710. float lightx[];
  711. {
  712.     int i, iEdge, thisEdge, firstEdge, thisSide, iFace;
  713.     Matrix rotOnlyMat;
  714.     float xformedNormal[3], lightToEye[3];
  715.     float lightToV1[3], lightToLastV1[3];
  716.     float shadowFaceNormal[3];
  717.     float worldV1[3], worldV2[3], lastV1[3], lastV2[3];
  718.     EdgePtr pEdge;
  719.     Boolean ingoing, lastIngoing, onceThrough;
  720.  
  721.     /* Get a rotation-only version of modeling transform for transforming
  722.        normals */
  723.     vmatcopy(xformMatrix, rotOnlyMat);
  724.     for (i=0; i<3; i++) rotOnlyMat[3][i] = 0.0;
  725.  
  726.     /* Mark each face for lightedness */
  727.     for (iFace = 0; iFace < myPDP->nFaces; iFace++) {
  728.     vtransform(myPDP->faces[iFace].n, rotOnlyMat, xformedNormal);
  729.     myPDP->faces[iFace].lit = vdot(xformedNormal, lightx) > 0.0;
  730.     }
  731.  
  732.     /* Evaluate and clear per-edge flags */
  733.     for (iEdge = 0, pEdge = myPDP->edges; iEdge < myPDP->nEdges;
  734.         iEdge++, pEdge++) {
  735.     pEdge->silhouetteFlag =
  736.             (pEdge->e[1].face < 0 ||
  737.         myPDP->faces[pEdge->e[0].face].lit !=
  738.         myPDP->faces[pEdge->e[1].face].lit);
  739.     pEdge->markedFlag = FALSE;
  740.     }
  741.  
  742.     /* For each silhouette edge, if it has not been projected yet,
  743.        trace a complete loop of silhouette edges, projecting shadow volumes */
  744.     /* iEdge is main edge list index; thisEdge, firstEdge, and pEdge trace
  745.        silhouette loops */
  746.     vsub(eyex, lightx, lightToEye);
  747.     for (iEdge = 0; iEdge<myPDP->nEdges; iEdge++) {
  748.  
  749.     /* If this is not a silhouette edge, or if we've projected it, skip */
  750.     pEdge = myPDP->edges + iEdge;
  751.     if (!pEdge->silhouetteFlag || pEdge->markedFlag) continue;
  752.  
  753.     firstEdge = thisEdge = iEdge;
  754.     thisSide = 0;
  755.     onceThrough = FALSE;
  756.     lastIngoing = -1;
  757.     if (showMode == showShadows) bgnqstrip();
  758.  
  759.     for (;;) {
  760.         /* Transform first edge vertex to world coordinates */
  761.         vtransform(myOP->t[pEdge->e[thisSide].vertex],
  762.             xformMatrix, worldV1);
  763.  
  764.         /* Project first edge vertex away from light to get a new vertex. */
  765.         vsub(worldV1, lightx, lightToV1);
  766.         vscale(lightToV1, SHADOWLENGTH/vlength(lightToV1));
  767.         vadd(worldV1, lightToV1, worldV2);
  768.  
  769.         if (onceThrough) {
  770.         /* Is this shadow face going into or out of shadow? */
  771.         vcross(lightToLastV1, lightToV1, shadowFaceNormal);
  772.         ingoing = vdot(shadowFaceNormal, lightToEye) > 0;
  773.         if (!myPDP->faces[pEdge->e[thisSide].face].lit)
  774.             ingoing = !ingoing;
  775.         }
  776.  
  777.         /* Set up and draw next 2 vertices of this shadow face quad */
  778.         if (onceThrough)
  779.         if (showMode == showShadows) {
  780.             if (lastIngoing != ingoing) {
  781.             endqstrip();
  782.             stencil(TRUE, 0, SF_ALWAYS, 0xff, ST_KEEP, ST_KEEP,
  783.                 ingoing?ST_INCR:ST_DECR);
  784.             bgnqstrip();
  785.             v3f(lastV1); v3f(lastV2);
  786.             lastIngoing = ingoing;
  787.             }
  788.             v3f(worldV1); v3f(worldV2);
  789.         }
  790.         else if (showMode == showMask) {
  791.             for (i=0; i<(ingoing?4:1); i++) {
  792.             stencil(TRUE, 0, SF_ALWAYS, 0xff, ST_KEEP, ST_KEEP,
  793.                 ST_INCR);
  794.             bgnpolygon();
  795.             v3f(lastV1); v3f(lastV2); v3f(worldV2); v3f(worldV1);
  796.             endpolygon();
  797.             }
  798.         }
  799.         else if (showMode == showVolumes) {
  800.             bgnpolygon();
  801.             v3f(lastV1); v3f(lastV2); v3f(worldV2); v3f(worldV1);
  802.             endpolygon();
  803.             if (showVolShadowEdges) {
  804.             /* Draw the edges of the shadow faces for clarity */
  805.             bgnclosedline();
  806.             v3f(lastV1); v3f(lastV2); v3f(worldV2); v3f(worldV1);
  807.             endclosedline();
  808.             }
  809.         }
  810.  
  811.         /* Exit if we've completed a full loop of sihouettes */
  812.         if (thisEdge == firstEdge && onceThrough) break;
  813.  
  814.         /* Update loop indices, pointers, and values */
  815.         pEdge->markedFlag = TRUE;
  816.         vcopy(lightToV1, lightToLastV1);
  817.         vcopy(worldV1, lastV1);
  818.         vcopy(worldV2, lastV2);
  819.         GetNextSilEdge(myPDP->edges, thisEdge, thisSide,
  820.         &thisEdge, &thisSide);
  821.         pEdge = myPDP->edges + thisEdge;
  822.         onceThrough = TRUE;
  823.     }
  824.  
  825.     if (showMode == showShadows) endqstrip();
  826.     }
  827. }
  828.  
  829.  
  830. void
  831. GetNextSilEdge(EdgePtr edgeList, int thisEdge, int thisSide,
  832.     int *pNextEdge, int *pNextSide) {
  833.     int nextEdge, nextSide;
  834.  
  835.     for (;;) {
  836.     nextEdge = edgeList[thisEdge].e[thisSide].nextEdge;
  837.     nextSide = edgeList[thisEdge].e[thisSide].nextSide;
  838.  
  839.     /* If the next edge around this polygon is a silhouette, we're done */
  840.     if (edgeList[nextEdge].silhouetteFlag) break;
  841.  
  842.     /* Otherwise, go to the next polygon and check the next edge. */
  843.     thisEdge = nextEdge;
  844.     thisSide = 1 - nextSide;
  845.     }
  846.     *pNextEdge = nextEdge;
  847.     *pNextSide = nextSide;
  848. }
  849.  
  850.  
  851. DrawSilEdges() {
  852.     /* For each object, transform and draw all silhouette edges, based
  853.        on lit flags set by DrawSilhouettes.  Yellow solid lines for
  854.        visible edges, dotted lines for those that fail z-buffer test.
  855.        Fudge the lsetdepth to help keep visible lines from breaking up. */
  856.  
  857.     int iObj, iEdge;
  858.     EdgePtr thisEdge;
  859.  
  860.     lsetdepth(0x000000, 0x7ffeff);
  861.     deflinestyle(1, 0xf0f0);
  862.  
  863.     for (iObj=0; iObj<nObjects; iObj++) {
  864.     pushmatrix();
  865.     translate(scene_posn[0], scene_posn[1], scene_posn[2]);
  866.     multmatrix(scene_rotMatrix);
  867.     translate(obj_posn[iObj][0], obj_posn[iObj][1], obj_posn[iObj][2]);
  868.     multmatrix(obj_rotMatrix[iObj]);
  869.     scale(obj_scale[iObj], obj_scale[iObj], obj_scale[iObj]);
  870.  
  871.     for (iEdge = 0, thisEdge = obj_data[iObj]->edges;
  872.         iEdge<obj_data[iObj]->nEdges; iEdge++, thisEdge++) {
  873.  
  874.         /* If this is a silhouette edge, draw it. */
  875.         if (thisEdge->silhouetteFlag) {
  876.  
  877.         /* Draw hidden, dashed lines first */
  878.         setlinestyle(1);
  879.         zfunction(ZF_GREATER);
  880.         bgnline();
  881.         v3f(obj_drawlist[iObj]->t[thisEdge->e[0].vertex]);
  882.         v3f(obj_drawlist[iObj]->t[thisEdge->e[1].vertex]);
  883.         endline();
  884.  
  885.         /* Then visible, solid lines next */
  886.         setlinestyle(0);
  887.         zfunction(ZF_LEQUAL);
  888.         bgnline();
  889.         v3f(obj_drawlist[iObj]->t[thisEdge->e[0].vertex]);
  890.         v3f(obj_drawlist[iObj]->t[thisEdge->e[1].vertex]);
  891.         endline();
  892.         }
  893.     }
  894.     popmatrix();
  895.     }
  896.     lsetdepth(0x000100, 0x7fffff);
  897. }
  898.  
  899. int
  900. JitterLights() {
  901.     int i, j;
  902.  
  903.     for (i=0; i<nLights; i++)
  904.     for (j=0; j<3; j++)
  905.         jitter_light_posn[i][j] = light_posn[i][j] +
  906.         ((random() & 0xffff)/32768.0 - 1.0) *
  907.         jitter_light_range[i][j];
  908. }
  909.  
  910. int
  911. RepositionLights() {
  912.     int i, j;
  913.  
  914.     for (i=0; i<nLights; i++)
  915.     for (j=0; j<3; j++)
  916.         jitter_light_posn[i][j] = light_posn[i][j];
  917. }
  918.